
#include "object.h"



/************************************************************ BASE OBJECT ************************************************************/
Object::Object( void ) {
  ID( 0 );
  type( 0 );
  vnum( 0 );
  basicFlags( 0 );
  flags( 0 );
  composition( 0 );
  level( 1 );
  value( 100 );
  shortname( "undefined" );
  return;
}

Object::Object( Area* area, const unsigned long& vnum, const unsigned short& type ) {
  basicFlags( 0 );
  flags( 0 );
  composition( 0 );
  level( 1 );
  value( 100 );
  shortname( "undefined" );
  try {
    char query[MAX_BUFFER];

    sprintf( query, "INSERT IGNORE INTO objects ( areaID, vnum, type ) VALUES ( %lu, %lu, %u );", area->ID(), vnum, type );
    World::Instance().getMysql()->insert( query );
    ID( World::Instance().getMysql()->getInsertID() );

    area->objects().insert( std::make_pair( vnum, this ) );
    this->vnum( vnum );
    this->type( type );

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to create object for area %lu: %s\n", area->ID(), me.getMessage().c_str() );
    return;
  }

  return;
}

Object::Object( const Object& ref ) {
  ID( ref.ID() );
  type( ref.type() );
  strtype( ref.strtype() );
  vnum( ref.vnum() );
  basicFlags( ref.basicFlags() );
  flags( ref.flags() );
  composition( ref.composition() );
  level( ref.level() );
  value( ref.value() );
  setKeywords( ref.getKeywords() );
  shortname( ref.shortname() );
  description( ref.description() );
  return;
}

Object::Object( ROW row ) {
  ID( row["objectID"] );
  type( row["type"] );
  vnum( row["vnum"] );
  basicFlags( row["basicFlags"] );
  flags( row["flags"] );
  composition( row["composition"] );
  level( row["level"] );
  value( row["value"] );
  unserializeKeywords( row["keywords"] );
  shortname( row["shortname"] );
  description( row["description"] );
  return;
}

Object::~Object( void ) {
  return;
}

void Object::type( const unsigned short& type ) { // public
  _type = type;
  switch( _type ) {
    case OTYPE_TRASH:     strtype( "trash" );     break;
    case OTYPE_CONTAINER: strtype( "container" ); break;
    case OTYPE_WEAPON:    strtype( "weapon" );    break;
    case OTYPE_ARMOR:     strtype( "armor" );     break;
    case OTYPE_KEY:       strtype( "key" );       break;
    default:              strtype( "undefined" ); break;
  }
  return;
}

void Object::save( void ) { // public
  try {
    char query[MAX_BUFFER];

    sprintf( query,
      "UPDATE objects SET     \
        basicFlags = %lu,     \
        flags = %lu,          \
        composition = %lu,    \
        level = %u,           \
        value = %u,           \
        keywords = '%s',      \
        shortname = '%s',     \
        description = '%s'    \
       WHERE objectID = %lu   \
       LIMIT 1;",
      basicFlags(),
      flags(),
      composition(),
      level(),
      value(),
      Mysql::addslashes(serializeKeywords()).c_str(),
      Mysql::addslashes(shortname()).c_str(),
      Mysql::addslashes(description()).c_str(),
      ID()
    );
    World::Instance().getMysql()->update( query );

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to save object %lu: %s\n", ID(), me.getMessage().c_str() );
    return;
  }
}

bool Object::destroy( void ) { // public
  unsigned long tempID = ID();
  try {
    char query[MAX_BUFFER];

    sprintf( query,
      " DELETE                \
        FROM objects          \
        WHERE objectID = %lu  \
        LIMIT 1;",
      ID()
    );
    World::Instance().getMysql()->remove( query );
    delete this;

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to delete object %lu: %s\n", tempID, me.getMessage().c_str() );
    return false;
  }
  return true;
}

std::string Object::listComposition( void ) { // public
  std::string output;
  unsigned long mask = 1;

  for ( unsigned long u = 0; u < sizeof(unsigned long)*8; ++u ) {
    if ( comp( mask ) ) {
      output.append( Object::getCompositionName( mask ) ).append( 1, ' ' );
    }
    mask <<= 1;
  }

  return output;
}

std::string Object::listBasicFlags( void ) { // public
  std::string output;
  unsigned long mask = 1;

  for ( unsigned long u = 0; u < sizeof(unsigned long)*8; ++u ) {
    if ( basicFlag( mask ) ) {
      output.append( Object::getFlagName( mask ) ).append( 1, ' ' );
    }
    mask <<= 1;
  }

  return output;
}

std::string Object::listFlags( void ) { // public
  std::string output;
  unsigned long mask = 1;

  for ( unsigned long u = 0; u < sizeof(unsigned long)*8; ++u ) {
    if ( flag( mask ) ) {
      output.append( Object::getFlagName( mask, type() ) ).append( 1, ' ' );
    }
    mask <<= 1;
  }

  return output;
}

Object* Object::create( Area* area, const unsigned long& vnum, const unsigned short& type ) { // public static
  switch ( type ) {
    case OTYPE_TRASH:     return new ObjTrash( area, vnum, type );
    case OTYPE_CONTAINER: return new ObjContainer( area, vnum, type );
    case OTYPE_WEAPON:    return new ObjWeapon( area, vnum, type );
    case OTYPE_ARMOR:     return new ObjArmor( area, vnum, type );
    case OTYPE_KEY:       return new ObjKey( area, vnum, type );
    default: return NULL;
  }
}

Object* Object::create( const unsigned short& type, ROW row ) { // public static
  switch ( type ) {
    case OTYPE_TRASH:     return new ObjTrash( row );
    case OTYPE_CONTAINER: return new ObjContainer( row );
    case OTYPE_WEAPON:    return new ObjWeapon( row );
    case OTYPE_ARMOR:     return new ObjArmor( row );
    case OTYPE_KEY:       return new ObjKey( row );
    default: return NULL;
  }
}

Object* Object::create( Object* src ) { // public static
  switch ( src->type() ) {
    case OTYPE_TRASH:     return new ObjTrash( *((ObjTrash*)src) );
    case OTYPE_CONTAINER: return new ObjContainer( *((ObjContainer*)src) );
    case OTYPE_WEAPON:    return new ObjWeapon( *((ObjWeapon*)src) );
    case OTYPE_ARMOR:     return new ObjArmor( *((ObjArmor*)src) );
    case OTYPE_KEY:       return new ObjKey( *((ObjKey*)src) );
    default: return NULL;
  }
}

std::string Object::getInformation( Object* object ) { // public static
  std::string output;
  char buffer[MAX_BUFFER];

  output.append( "  --== {Ybasic object data{x ==--\n" );
  // Basic object information...
  sprintf( buffer, "vnum......... {y%lu{x\n\
type......... {y%s{x\n\
flags........ {y%s{x\n\
composition.. {y%s{x\n\
level........ {y%u{x\n\
value........ {y%u{x\n\
keywords..... {y%s{x\n\
shortname.... %s\n\
description.. %s\n\
", object->vnum(), object->strtype().c_str(), object->listBasicFlags().c_str(), object->listComposition().c_str(), object->level(),
   object->value(), object->getKeywordList().c_str(), object->shortname().c_str(), object->description().c_str() );
  output.append( buffer );

  // Type-specific data...
  sprintf( buffer, "\n  --== {Y%s data{x ==--\n", object->strtype().c_str() );
  output.append( buffer );
  sprintf( buffer, "flags: {y%s{x\n", object->listFlags().c_str() );
  output.append( buffer );
  switch ( object->type() ) {
    case OTYPE_TRASH:
      break;
    case OTYPE_CONTAINER:
      sprintf( buffer, "Contains {y%u{x items.\n", (unsigned)((ObjContainer*)object)->objects().size() );
      output.append( buffer );
      break;
    case OTYPE_WEAPON:
      break;
    case OTYPE_ARMOR:
      break;
    case OTYPE_KEY:
      break;
    default:
      output.append( "{RERROR{x" );
      break;
  }

  return output;
}

unsigned short Object::getType( const std::string& t ) { // public static
  if ( Regex::strPrefix( t, "trash" ) ) {
    return OTYPE_TRASH;
  } else if ( Regex::strPrefix( t, "container" ) ) {
    return OTYPE_CONTAINER;
  } else if ( Regex::strPrefix( t, "weapon" ) ) {
    return OTYPE_WEAPON;
  } else if ( Regex::strPrefix( t, "armor" ) ) {
    return OTYPE_ARMOR;
  } else if ( Regex::strPrefix( t, "key" ) ) {
    return OTYPE_KEY;
  } else {
    return OTYPE_UNDEFINED;
  }
}

std::string Object::getType( const unsigned short& t ) { // public static
  switch ( t ) {
    case OTYPE_TRASH:     return "trash";
    case OTYPE_CONTAINER: return "container";
    case OTYPE_WEAPON:    return "weapon";
    case OTYPE_ARMOR:     return "armor";
    case OTYPE_KEY:       return "key";
    default:              return "undefined";
  }
}

const char* Object::getFlagName( const unsigned long& flag, unsigned short type ) { // public static
  switch ( type ) {
    case OTYPE_TRASH:
      switch ( flag ) {
        default:              return "undefined";
      }
      break;
    case OTYPE_CONTAINER:
      switch ( flag ) {
        case OFLAG_CLOSEABLE: return "closeable";
        case OFLAG_CLOSED:    return "closed";
        case OFLAG_LOCKABLE:  return "lockable";
        case OFLAG_LOCKED:    return "locked";
        case OFLAG_BURNPROOF: return "burnproof";
        default:              return "undefined";
      }
      break;
    case OTYPE_WEAPON:
      switch ( flag ) {
        case OFLAG_SHARP:     return "sharp";
        case OFLAG_SHOCKING:  return "shocking";
        case OFLAG_BURNING:   return "burning";
        case OFLAG_FREEZING:  return "freezing";
        default:              return "undefined";
      }
      break;
    case OTYPE_ARMOR:
      switch ( flag ) {
        default:              return "undefined";
      }
      break;
    case OTYPE_KEY:
      switch ( flag ) {
        default:              return "undefined";
      }
      break;
    default:
      switch ( flag ) {
        case OFLAG_NOREMOVE:  return "noremove";
        case OFLAG_NODROP:    return "nodrop";
        case OFLAG_MELTDROP:  return "meltdrop";
        case OFLAG_GLOWING:   return "glowing";
        case OFLAG_HUMMING:   return "humming";
        default:              return "undefined";
      }
      break;
  }
}

unsigned long Object::getFlagType( const std::string& name, unsigned short type ) { // public static
  switch ( type ) {
    case OTYPE_TRASH:
      return OFLAG_UNDEFINED;
      break;
    case OTYPE_CONTAINER:
      if ( Regex::strPrefix( name, "closeable" ) ) {
        return OFLAG_CLOSEABLE;
      } else if ( Regex::strPrefix( name, "closed" ) ) {
        return OFLAG_CLOSED;
      } else if ( Regex::strPrefix( name, "lockable" ) ) {
        return OFLAG_LOCKABLE;
      } else if ( Regex::strPrefix( name, "locked" ) ) {
        return OFLAG_LOCKED;
      } else if ( Regex::strPrefix( name, "burnproof" ) ) {
        return OFLAG_BURNPROOF;
      } else {
        return OFLAG_UNDEFINED;
      }
      break;
    case OTYPE_WEAPON:
      if ( Regex::strPrefix( name, "sharp" ) ) {
        return OFLAG_SHARP;
      } else if ( Regex::strPrefix( name, "shocking" ) ) {
        return OFLAG_SHOCKING;
      } else if ( Regex::strPrefix( name, "burning" ) ) {
        return OFLAG_BURNING;
      } else if ( Regex::strPrefix( name, "freezing" ) ) {
        return OFLAG_FREEZING;
      } else {
        return OFLAG_UNDEFINED;
      }
      break;
    case OTYPE_ARMOR:
      return OFLAG_UNDEFINED;
      break;
    case OTYPE_KEY:
      return OFLAG_UNDEFINED;
      break;
    default:
      if ( Regex::strPrefix( name, "noremove" ) ) {
        return OFLAG_NOREMOVE;
      } else if ( Regex::strPrefix( name, "nodrop" ) ) {
        return OFLAG_NODROP;
      } else if ( Regex::strPrefix( name, "meltdrop" ) ) {
        return OFLAG_MELTDROP;
      } else if ( Regex::strPrefix( name, "glowing" ) ) {
        return OFLAG_GLOWING;
      } else if ( Regex::strPrefix( name, "humming" ) ) {
        return OFLAG_HUMMING;
      } else {
        return OFLAG_UNDEFINED;
      }
      break;
  }
}

const char* Object::getCompositionName( const unsigned long& composition ) { // public static
  switch ( composition ) {
    case OCOMP_WOOD:    return "wood";
    case OCOMP_PAPER:   return "paper";
    case OCOMP_IRON:    return "iron";
    case OCOMP_LEATHER: return "leather";
    default:            return "undefined";
  }
}

unsigned long Object::getCompositionType( const std::string& composition ) { // public static
  if ( Regex::strPrefix( composition, "wood" ) ) {
    return OCOMP_WOOD;
  } else if ( Regex::strPrefix( composition, "paper" ) ) {
    return OCOMP_PAPER;
  } else if ( Regex::strPrefix( composition, "iron" ) ) {
    return OCOMP_IRON;
  } else if ( Regex::strPrefix( composition, "leather" ) ) {
    return OCOMP_LEATHER;
  } else {
    return OCOMP_UNDEFINED;
  }
}

/************************************************************ TRASH ************************************************************/
ObjTrash::ObjTrash( void ): Object() {
  return;
}

ObjTrash::ObjTrash( Area* area, const unsigned long& vnum, const unsigned short& type ): Object( area, vnum, type ) {
  return;
}

ObjTrash::ObjTrash( const ObjTrash& ref ): Object( ref ) {
  return;
}

ObjTrash::ObjTrash( ROW row ): Object( row ) {
  return;
}

ObjTrash::~ObjTrash( void ) {
  return;
}

/************************************************************ CONTAINER ************************************************************/
ObjContainer::ObjContainer( void ): Object() {
  return;
}

ObjContainer::ObjContainer( Area* area, const unsigned long& vnum, const unsigned short& type ): Object( area, vnum, type ) {
  return;
}

ObjContainer::ObjContainer( const ObjContainer& ref ): Object( ref ) {
  return;
}

ObjContainer::ObjContainer( ROW row ): Object( row ) {
  return;
}

ObjContainer::~ObjContainer( void ) {
  for ( std::list<Object*>::iterator it = objects().begin(); it != objects().end(); ++it ) {
    delete *it;
  }
  objects().clear();
  return;
}

Object* ObjContainer::find( const std::string& name ) { // public
  for ( std::list<Object*>::iterator it = objects().begin(); it != objects().end(); ++it ) {
    if ( (*it)->matchesKeyword( name ) ) {
      return *it;
    }
  }
  return NULL;
}

/************************************************************ WEAPON ************************************************************/
ObjWeapon::ObjWeapon( void ): Object() {
  return;
}

ObjWeapon::ObjWeapon( Area* area, const unsigned long& vnum, const unsigned short& type ): Object( area, vnum, type ) {
  return;
}

ObjWeapon::ObjWeapon( const ObjWeapon& ref ): Object( ref ) {
  return;
}

ObjWeapon::ObjWeapon( ROW row ): Object( row ) {
  return;
}

ObjWeapon::~ObjWeapon( void ) {
  return;
}

/************************************************************ ARMOR ************************************************************/
ObjArmor::ObjArmor( void ): Object() {
  return;
}

ObjArmor::ObjArmor( Area* area, const unsigned long& vnum, const unsigned short& type ): Object( area, vnum, type ) {
  return;
}

ObjArmor::ObjArmor( const ObjArmor& ref ): Object( ref ) {
  return;
}

ObjArmor::ObjArmor( ROW row ): Object( row ) {
  return;
}

ObjArmor::~ObjArmor( void ) {
  return;
}

/************************************************************ KEY ************************************************************/
ObjKey::ObjKey( void ): Object() {
  return;
}

ObjKey::ObjKey( Area* area, const unsigned long& vnum, const unsigned short& type ): Object( area, vnum, type ) {
  return;
}

ObjKey::ObjKey( const ObjKey& ref ): Object( ref ) {
  return;
}

ObjKey::ObjKey( ROW row ): Object( row ) {
  return;
}

ObjKey::~ObjKey( void ) {
  return;
}

